package com.github.gaojh.starter.rabbitmq;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.github.gaojh.ioc.IocUtil;
import com.github.gaojh.ioc.annotation.Autowired;
import com.github.gaojh.ioc.annotation.Bean;
import com.github.gaojh.ioc.annotation.Configuration;
import com.github.gaojh.starter.ServerFace;
import com.github.gaojh.starter.rabbitmq.annotation.RabbitConsumer;
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @author gaojh
 * @since 2020/4/14
 */
@Configuration
@Slf4j
public class RabbitStarter implements ServerFace {

    @Autowired
    private RabbitProperties rabbitProperties;

    private Connection connection;

    private RabbitContext rabbitContext;

    @Bean
    public Connection connection() throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(rabbitProperties.getHost());
        factory.setPort(rabbitProperties.getPort());
        if (rabbitProperties.getUsername() != null) {
            factory.setUsername(rabbitProperties.getUsername());
        }
        if (rabbitProperties.getPassword() != null) {
            factory.setPassword(rabbitProperties.getPassword());
        }
        if (rabbitProperties.getVirtualHost() != null) {
            factory.setVirtualHost(rabbitProperties.getVirtualHost());
        }
        this.connection = factory.newConnection();
        return this.connection;
    }

    @Bean
    public RabbitProducer rabbitProducer(RabbitContext rabbitContext) {
        return new RabbitProducer(rabbitContext);
    }

    @Bean
    public RabbitContext rabbitContext(Connection connection) {
        this.rabbitContext = new RabbitContext(connection);
        return this.rabbitContext;
    }

    @Override
    public boolean start() throws Exception {
        Channel channel = connection.createChannel();
        List<Queue> queueList = IocUtil.getBeansByType(Queue.class);
        for (Queue queue : queueList) {
            channel.queueDeclare(queue.getQueue(), queue.isDurable(), queue.isExclusive(), queue.isAutoDelete(), queue.getArguments());
        }

        List<Exchange> exchangeList = IocUtil.getBeansByType(Exchange.class);
        for (Exchange exchange : exchangeList) {
            channel.exchangeDeclare(exchange.getExchange(), exchange.getType(), exchange.isDurable(), exchange.isAutoDelete(), exchange.getArguments());
        }

        List<Binding> bindingList = IocUtil.getBeansByType(Binding.class);
        for (Binding binding : bindingList) {
            channel.queueBind(binding.getQueue().getQueue(), binding.getExchange().getExchange(), binding.getRoutingKey(), binding.getArguments());
        }
        channel.close();


        List<RabbitConsumerAdapter> consumerAdapters = IocUtil.getBeansByInterface(RabbitConsumerAdapter.class);
        for (RabbitConsumerAdapter adapter : consumerAdapters) {
            RabbitConsumer rabbitConsumer = adapter.getClass().getAnnotation(RabbitConsumer.class);
            if (rabbitConsumer == null) {
                log.error("{} 未配置 @RabbitConsumer注解，忽略", adapter.getClass().getName());
                continue;
            }

            if (StrUtil.isBlank(rabbitConsumer.queue())) {
                log.error("{} 未设置queue值，忽略", adapter.getClass().getName());
            }
            Channel c = rabbitContext.getChannel(rabbitConsumer.queue());
            ThreadUtil.execute(() -> {
                try {
                    c.basicConsume(rabbitConsumer.queue(), rabbitConsumer.autoAck(), rabbitConsumer.consumerTag(), rabbitConsumer.noLocal(), rabbitConsumer.exclusive(), null, new DefaultConsumer(c) {
                        @Override
                        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                            try {
                                adapter.onMessage(consumerTag, new String(body, StandardCharsets.UTF_8));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }

        return true;
    }

    @Override
    public boolean stop() throws Exception {
        this.connection.close();
        for (Channel channel : this.rabbitContext.getAllChannel()) {
            channel.close();
        }
        return true;
    }
}
